package com.jucStudy.threadlocalStudy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.stream.IntStream;

/**
 * 这个类的目的是为了学习线程数据隔离性问题
 *
 * 多个线程间操作一个公共数据会出现什么情况？
 *
 * 出现的情况该怎么解释？
 *
 * 该怎么解决？
 *
 * 解决之后还会存在隐患么？
 *
 * 存在隐患的话该怎么解决？
 *
 */
@SpringBootApplication
public class ThreadIIsolationIssue {
    public static void main(String[] args) throws ParseException {

        SpringApplication.run(ThreadIIsolationIssue.class,args);
        //测试多线程操作原生公共资源会产生什么问题
//        PublicSource publicSource = new PublicSource();
//        publicSource.testThreadsIssues();
//
//        //被操作资源添加threadlocal之后出现的情况
//        AddThreadlocalPublicSource addThreadlocalPublicSource = new AddThreadlocalPublicSource();
//        addThreadlocalPublicSource.testThreadsIssues();
//
//        //子线程读取父线程threadlocal数据
//        InheritPublicSource inheritPublicSource = new InheritPublicSource();
//        inheritPublicSource.testThreadsIssues();
//
//        //解决子线程无法读取父线程threadlocal数据的问题
//        SubInheritPublicSource subInheritPublicSource = new SubInheritPublicSource();
//        subInheritPublicSource.testThreadsIssues();
//
//        //显示循环打印
//        PrintClass printClass = new PrintClass();
//        printClass.printMsg();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
        Date parse = simpleDateFormat.parse("");
    }
}

/**
 * 原生的公共资源类
 */
class PublicSource{
    private String sources;

    public void setSources(String sources) {
        this.sources = sources;
    }

    public String getSources() {
        return sources;
    }

    /**
     * 多线程操作原生公共资源类
     *
     * 该方法的执行结果：
     * Thread-0======第3个线程的数据
     * Thread-3======第5个线程的数据
     * Thread-2======第4个线程的数据
     * Thread-1======第3个线程的数据
     * Thread-5======第8个线程的数据
     * Thread-6======第8个线程的数据
     * Thread-4======第7个线程的数据
     * Thread-7======第9个线程的数据
     * Thread-8======第10个线程的数据
     * Thread-9======第10个线程的数据
     *
     * 问题：多个线程操作公共资源类出现了这个线程读取到另一个线程数据的情况，出现了线程数据隔离性问题
     *
     * 解释：线程并不独立拥有用户空间，用户空间是归进程所有的，同一进程中所有线程所共享
     *      多线程操作共享变量的流程图
     *     https://www.processon.com/diagraming/61246873e0b34d5c8a33bcfc
     *
     *  怎么解决这种问题呢？设想一下
     *  首先多个线程操作的是一个线程共享变量，每次修改绝对是不一样的，那么我们可以将共享区间的数据拷贝一份进行操作这样就能够实现每个线程获取每个线程自己的变量
     *
     *  java中有具体的实现  Threadlocal 线程本地变量
     *
     *  Threadlocal使用前提是什么？
     *  必须是被多个线程所操作的公共变量
     */
    public void testThreadsIssues(){
        PublicSource publicSource = new PublicSource();

        for (int i = 0; i < 10; i++) {
            publicSource.setSources("第"+(i+1)+"个线程的数据");
            new Thread(()-> System.out.println(Thread.currentThread().getName()+"======"+publicSource.getSources())).start();
        }

    }
}

/**
 * 解决线程数据隔离问题
 */
class AddThreadlocalPublicSource{
    private final ThreadLocal<String> sources = new ThreadLocal<>();

    public void setSources(String sources) {
        this.sources.set(sources);
    }

    public String getSources() {
        return this.sources.get();
    }

    /**
     * 被多线程操作的全局变量添加threadlocal之后的多线程操作方法
     */
    public void testThreadsIssues(){
        AddThreadlocalPublicSource publicSource = new AddThreadlocalPublicSource();

        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(()-> {
                publicSource.setSources("第"+(finalI)+"个线程的数据");
                String sources = publicSource.getSources();
                System.out.println(Thread.currentThread().getName()+"======"+sources);
            }).start();
        }

    }
}

/**
 * 这种的无法实现子线程读取父线程的threadlocal值
 */
class InheritPublicSource{
    private final ThreadLocal<String> sources = new ThreadLocal<>();

    public void setSources(String sources) {
        this.sources.set(sources);
    }

    public String getSources() {
        return this.sources.get();
    }

    /**
     * 被多线程操作的全局变量添加threadlocal之后的多线程操作方法
     */
    public void testThreadsIssues(){
        InheritPublicSource publicSource = new InheritPublicSource();

        for (int i = 0; i < 10; i++) {
            publicSource.setSources("第"+(i)+"个线程的数据");
            new Thread(()-> {
                String sources = publicSource.getSources();
                System.out.println(Thread.currentThread().getName()+"======"+sources);
            }).start();
        }

    }
}


class SubInheritPublicSource{
    private final InheritableThreadLocal<String> sources = new InheritableThreadLocal<>();

    public void setSources(String sources) {
        this.sources.set(sources);
    }

    public String getSources() {
        return this.sources.get();
    }

    /**
     * 被多线程操作的全局变量添加threadlocal之后的多线程操作方法
     */
    public void testThreadsIssues(){
        SubInheritPublicSource publicSource = new SubInheritPublicSource();

        for (int i = 0; i < 10; i++) {
            publicSource.setSources("第"+(i)+"个线程的数据");
            new Thread(()-> {
                String sources = publicSource.getSources();
                System.out.println(Thread.currentThread().getName()+"======"+sources);
            }).start();
        }

    }
}

class PrintClass{
    public void printMsg(){
        //[左面,右面)
        IntStream.range(0,10).forEach(i->{
            System.out.println("i = " + i);
        });
        //初始化SimpleFormatter
        ThreadLocal<SimpleDateFormat> simpleFormatterThreadLocal = ThreadLocal.withInitial(SimpleDateFormat::new);

    }
}



